#ifndef INDIVIDUAL_H
#define INDIVIDUAL_H

#include "physics_engine.h"

#include <vector>
#include <btBulletDynamicsCommon.h>

#include <Graph.h>


class Individual;
class Member;
class Connection;

//The Connection describes the link between individual's parts
// - the link constraint is described as a btTypedConstraint
// - the connected part is described as an Member
class Connection
{
    //ctors
public:
    PHYSICS_ENGINE_API explicit Connection(btTypedConstraint * connection_constraint, Member * connected_member);
    PHYSICS_ENGINE_API ~Connection(void);

    //accessors
public:
    PHYSICS_ENGINE_API inline btTypedConstraint& constraint(void) { return *m_constraint; }
    PHYSICS_ENGINE_API inline Member& connectedMember(void) { return *m_member; }

    //state
private:
    btTypedConstraint * m_constraint;
    Member *            m_member;
};

//The Member class describes a single part of an individual
// this part contains a body (implemented as a btRigidBody)
// and thus: a position, a velocity, a mass, a friction factor, a restitution factor and a (collision) shape
// a part can be linked to another part via a constraint (implemented as a btTypedConstraint)
class Member
{
    //definition
public:
    typedef std::vector<Connection> ConnectionCollection;

    //ctors
public:
    PHYSICS_ENGINE_API explicit Member(const btRigidBody &main_body);
    PHYSICS_ENGINE_API ~Member(void);

    PHYSICS_ENGINE_API void connectTo(const Connection& connection);

    //query state
public:
    PHYSICS_ENGINE_API inline btRigidBody& body(void) { return m_body; }
    PHYSICS_ENGINE_API inline btVector3    position(void) const { return m_body.getWorldTransform().getOrigin(); }
    PHYSICS_ENGINE_API inline btVector3    velocity(void) const { return m_body.getLinearVelocity(); }

    PHYSICS_ENGINE_API float visionRadius();
    PHYSICS_ENGINE_API bool  hitGround();

    PHYSICS_ENGINE_API bool distanceFromGoal(btVector3 &distance);

    //Bullet physics library - State
private:
    btRigidBody           m_body;
    ConnectionCollection  m_connections;
};

class MotionStateFactory
{
public:
	virtual btMotionState* create(const btTransform& initialPosition, const btVector3&   initialDimension) = 0;
};

//The Individual class describes an individual that must pass an obstacles in our simulation
// Its creation is specified via a graph to configure its members and connections
// The connection and evolution in the simulation world is enabled via bullet library (RigidBody ? SoftBody ?)
// Several sensors and effectors are available to control its reaction within the SimulationWorld
// Other class might be necessary to describe members state and connections state (dimension, shape, etc)
// NOTE: No objects smaller than 10 centimeters or greater than 5 meters
// NOTE: Best to keep mass around 1
class Individual
{
    //ctors
private:
    //NOTE: these may be not accessible in 'public' if we want to restrain Individual construction only by Graph'
    PHYSICS_ENGINE_API explicit Individual(const Member& root);

public:
    PHYSICS_ENGINE_API static Individual fromGraph(const Graph& graph, MotionStateFactory* factory);

    //accessors
public:
    PHYSICS_ENGINE_API long id() const;

    //State
private:
    long   m_id;
    Member m_root_member;
};

class IndividualIdOrdering
{
public:
    PHYSICS_ENGINE_API bool operator()(const Individual* lhs, const Individual* rhs) const
    {
        return lhs->id() < rhs->id();
    }
};

#endif // INDIVIDUAL_H
